package srg

import (
	"encoding/json"
	"fmt"
	"time"
)

var defFlowQueue string
var defFlowClearQueue string

const oriFlowHead = "ORI.FLOW"

const deadFlowHead = "DEAD.FLOW"

var srgFlowWorkers []SrgFlowWorker

type FlowWork struct {
	FlowType     string `json:"flowType"`
	FlowInfo     string `json:"flowInfo"`
	ReturnInfo   string `json:"returnInfo"`   //返回信息
	ReturnResult bool   `json:"returnResult"` //返回结果
}

type SrgFlowWork struct {
	Id          int64      `json:"id"`          //流程id
	FlowWorks   []FlowWork `json:"flowWorks"`   //子流程
	CurIndex    int        `json:"curIndex"`    //当前执行索引
	FailTime    int64      `json:"failTime"`    //整个流程失败时间
	RollBacking bool       `json:"rollBacking"` //是否回滚中
	FailMsg     string     `json:"failMsg"`     //整个流程失败信息

}

type SrgFlowWorker struct {
	MatchFlowType    func(flowType string) bool
	FlowWork         func(flowWork FlowWork) (string, error)
	FlowRollBackWork func(flowWork FlowWork) (string, error)
}

// 流程添加
// firstInfo 第一个流程信息
// failTime 失败时间
// flowTypes 流程类型
func FlowWorkAdd(firstInfo string, failTime int64, flowTypes ...string) {
	flowWork := SrgFlowWork{
		FlowWorks: []FlowWork{
			{
				FlowType:     flowTypes[0],
				FlowInfo:     firstInfo,
				ReturnResult: true,
			},
		},
		FailTime: failTime,
	}
	for ii, v := range flowTypes {
		if ii == 0 {
			continue
		}
		flowWork.FlowWorks = append(flowWork.FlowWorks, FlowWork{
			FlowType: v,
			FlowInfo: "",
		})
	}
	flowWorkAdd(flowWork)
}

func flowWorkAdd(flowWork SrgFlowWork) {

	flowWork.Id = GetUniqueId()
	flowWork.RollBacking = false
	flowWork.CurIndex = 0

	flowWorkBytes, _ := json.Marshal(flowWork)

	srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id), string(flowWorkBytes), 0)

	srds.Publish(ctx, defFlowQueue, flowWork.Id)
}

// 注册新的流程处理者
func RegisterFlowWorker(worker SrgFlowWorker) {
	srgFlowWorkers = append(srgFlowWorkers, worker)
}

// 监听系统
func listenFlow() {
	pubsub := srds.Subscribe(ctx, defFlowQueue)
	defer pubsub.Close()
	ch := pubsub.Channel()
	for msg := range ch {

		go delFlow(msg.Payload)
	}
}

// 推进流程
// msgPayload 消息id
func delFlow(msgPayload string) {

	flowWorkBytes, _ := srds.Get(ctx, fmt.Sprintf("%s%s:%s", srgHead, oriFlowHead, msgPayload)).Result()
	if flowWorkBytes == "" {
		return
	}

	var flowWork SrgFlowWork
	json.Unmarshal([]byte(flowWorkBytes), &flowWork)

	if flowWork.CurIndex < 0 { //回滚结束

		//抢夺处理权
		locked := DLockAdd(fmt.Sprintf("%s%s:%d", srgHead, "FLOW_LOCK:", flowWork.Id), time.Second*6)
		defer DLockRelease(fmt.Sprintf("%s%s:%d", srgHead, "FLOW_LOCK:", flowWork.Id))
		if !locked {
			return
		}
		srds.Del(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id))
		//TODO 发送回滚结束通知
		fmt.Println("发送回滚结束通知")

		return
	}

	//匹配处理者

	var flowWorker SrgFlowWorker

	for _, v := range srgFlowWorkers {

		if v.MatchFlowType(flowWork.FlowWorks[flowWork.CurIndex].FlowType) {
			flowWorker = v
			break
		}
	}
	if flowWorker.FlowWork == nil { //没有处理者 不抢
		return
	}

	//抢夺处理权
	locked := DLockAdd(fmt.Sprintf("%s%s:%d", srgHead, "FLOW_LOCK:", flowWork.Id), time.Second*6)
	defer DLockRelease(fmt.Sprintf("%s%s:%d", srgHead, "FLOW_LOCK:", flowWork.Id))
	if !locked {
		return
	}
	//处理流程
	if !flowWork.RollBacking {

		//判断时间是否过期
		if flowWork.FailTime < time.Now().UnixMilli() {

			flowWork.RollBacking = true

			//往下推进
			flowWorkBytes, _ := json.Marshal(flowWork)
			srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id), string(flowWorkBytes), 0)
			srds.Publish(ctx, defFlowQueue, flowWork.Id)

			//TODO 发送错误回滚通知
			fmt.Println("发送错误回滚通知")
			return
		}

		returnInfo, err := flowWorker.FlowWork(flowWork.FlowWorks[flowWork.CurIndex])
		if err == nil {
			//正流程无错误
			flowWork.CurIndex = flowWork.CurIndex + 1

			if flowWork.CurIndex >= len(flowWork.FlowWorks) { //流程执行完毕

				srds.Del(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id))
				//TODO 发送完毕通知

				fmt.Println("发送完毕通知")

			} else {
				flowWork.FlowWorks[flowWork.CurIndex].ReturnInfo = returnInfo
				flowWork.FlowWorks[flowWork.CurIndex].ReturnResult = true

				//往下推进
				flowWorkBytes, _ := json.Marshal(flowWork)
				srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id), string(flowWorkBytes), 0)
				srds.Publish(ctx, defFlowQueue, flowWork.Id)

				//TODO 发送推进通知

				fmt.Println("发送推进通知")
			}

		} else { //正流程有错误
			flowWork.RollBacking = true

			//往下推进
			flowWorkBytes, _ := json.Marshal(flowWork)
			srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id), string(flowWorkBytes), 0)
			srds.Publish(ctx, defFlowQueue, flowWork.Id)

			//TODO 发送错误回滚通知
			fmt.Println("发送错误回滚通知")
		}

	} else {

		returnInfo, err := flowWorker.FlowRollBackWork(flowWork.FlowWorks[flowWork.CurIndex])

		flowWork.CurIndex = flowWork.CurIndex - 1
		if err == nil { //回滚正常

			//往下推进
			flowWorkBytes, _ := json.Marshal(flowWork)
			srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id), string(flowWorkBytes), 0)
			srds.Publish(ctx, defFlowQueue, flowWork.Id)

			//TODO 发送错误回滚推进通知
			fmt.Println("发送回滚推进通知")

		} else { //回滚错误

			//进流式死信
			flowWork.FlowWorks[flowWork.CurIndex].ReturnInfo = returnInfo
			flowWork.FlowWorks[flowWork.CurIndex].ReturnResult = false
			flowWork.FailMsg = "回滚错误 " + err.Error()
			flowWorkBytes, _ := json.Marshal(flowWork)
			srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, deadFlowHead, flowWork.Id), string(flowWorkBytes), 0)
			srds.Del(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriFlowHead, flowWork.Id))

			//TODO 发送错误回滚错误通知
			fmt.Println("发送回滚错误通知")
		}
	}

}

// 处理之前的流程
func dealExFlow() {

	flowWorkIds, _ := srds.Keys(ctx, fmt.Sprintf("%s%s:*", srgHead, oriFlowHead)).Result()
	for _, id := range flowWorkIds {

		//有过期的处理下
		flowWorkBytes, _ := srds.Get(ctx, id).Result()
		if flowWorkBytes == "" {
			continue
		}

		var flowWork SrgFlowWork
		json.Unmarshal([]byte(flowWorkBytes), &flowWork)

		//判断时间是否过期
		if flowWork.FailTime > time.Now().UnixMilli() {
			continue
		}

		//往下推进
		srds.Publish(ctx, defFlowQueue, flowWork.Id)
	}

}
